#include "patches.hpp"


namespace BSP2TMRF
{
	void ComputeSTVecs(const FVec3 &normal, const FVec3 &p0to1, const FVec3 &p0to2,
		const FVec2 &tc0to1, const FVec2 &tc0to2, FVec3 *svec, FVec3 *tvec)
	{
        float r = 1.0f / (tc0to1[0] * tc0to2[1] - tc0to2[0] * tc0to1[1]);
        *svec = ((p0to1 * tc0to2[1] - p0to2 * tc0to1[1]) * r).Normalize2();
        *tvec = ((p0to2 * tc0to1[0] - p0to1 * tc0to2[0]) * r).Normalize2();
	}

	float CompressSTVec(const FVec3 &normal, const FVec3 &svec, const FVec3 &tvec)
	{
		FVec3 cross = svec.Cross(tvec);
		float dist = cross.DotProduct(normal);
		if(dist > 0.0)
			return 1.0f;
		else
			return -1.0f;
	}

	template<class _T>
	_T BSPEvaluateLinear(const _T &point1, const _T &point2, const _T &point3, float t)
	{
		return point1 * (1.0f - t) + point3 * t;
	}

	template<class _T>
	_T BSPEvaluateSpline(const _T &point1, const _T &point2, const _T &point3, float t)
	{
		float tSquared = t*t;
		float tDoubled = t+t;

		return point1 * (1.0f - tDoubled + tSquared) +
			 point2 * (tDoubled - 2.0f*tSquared) +
			 point3 * tSquared;
	}

	template<class _T>
	_T BSPEvaluateSplineDerivative(const _T &point1, const _T &point2, const _T &point3, float t)
	{
		return point1 * (-1.0f + t) + point2 * (1.0f - 2.0f * t) + point3 * t;
	}

	template<class _T>
	_T BSPEvaluate2DSpline(const _T *controlPoints, float x, float y)
	{
		_T p1, p2, p3;

		p1 = BSPEvaluateSpline<_T>(controlPoints[0], controlPoints[1], controlPoints[2], x);
		p2 = BSPEvaluateSpline<_T>(controlPoints[3], controlPoints[4], controlPoints[5], x);
		p3 = BSPEvaluateSpline<_T>(controlPoints[6], controlPoints[7], controlPoints[8], x);

		return BSPEvaluateSpline<_T>(p1, p2, p3, y);
	}

	template<class _T>
	_T BSPEvaluate2DSplineDerivativeHorizontal(const _T *controlPoints, float x, float y)
	{
		_T p1, p2, p3;

		p1 = BSPEvaluateSpline<_T>(controlPoints[0], controlPoints[3], controlPoints[6], y);
		p2 = BSPEvaluateSpline<_T>(controlPoints[1], controlPoints[4], controlPoints[7], y);
		p3 = BSPEvaluateSpline<_T>(controlPoints[2], controlPoints[5], controlPoints[8], y);

		return BSPEvaluateSplineDerivative(p1, p2, p3, x);
	}

	template<class _T>
	_T BSPEvaluate2DSplineDerivativeVertical(const _T *controlPoints, float x, float y)
	{
		_T p1, p2, p3;

		p1 = BSPEvaluateSpline<_T>(controlPoints[0], controlPoints[1], controlPoints[2], x);
		p2 = BSPEvaluateSpline<_T>(controlPoints[3], controlPoints[4], controlPoints[5], x);
		p3 = BSPEvaluateSpline<_T>(controlPoints[6], controlPoints[7], controlPoints[8], x);

		return BSPEvaluateSplineDerivative(p1, p2, p3, y);
	}

	template<class _T>
	_T BSPEvaluate2DLinear(const _T *controlPoints, float x, float y)
	{
		_T p1, p2, p3;

		p1 = BSPEvaluateLinear<_T>(controlPoints[0], controlPoints[1], controlPoints[2], x);
		p2 = BSPEvaluateLinear<_T>(controlPoints[3], controlPoints[4], controlPoints[5], x);
		p3 = BSPEvaluateLinear<_T>(controlPoints[6], controlPoints[7], controlPoints[8], x);

		return BSPEvaluateLinear<_T>(p1, p2, p3, y);
	}

	template<class _T>
	void BSPStitchPoints(_T *a, _T *b)
	{
		*a = *b = (*a) + (*b);
	}

	unsigned int BSPGetPatchDivisionsLinear(const FVec3 &p1, const FVec3 &p2, const FVec3 &p3, float maxTolerance)
	{
		unsigned int subdivs = 1;
		float length = (p3 - p1).Length();

		if(length < 1.0f) return 1;	// Miniscule

		FVec3 splineMidpoint = BSPEvaluateSpline<FVec3>(p1, p2, p3, 0.5f);

		// Find the closest point on the linear path to the spline midpoint
		FVec3 projectionPlane = (p3 - p1).Normalize();
		FVec3 projectedPoint = p1 + projectionPlane * (projectionPlane.DotProduct(splineMidpoint) - projectionPlane.DotProduct(p1));

		// See how far off this was
		float dist = (projectedPoint - splineMidpoint).Length() / length;

		subdivs = static_cast<unsigned int>(dist / maxTolerance);
		return subdivs + 1;
	}


	void BSPPatchStitchPoint::TouchDivisions(unsigned int d)
	{
		if(d > divisions)
			divisions = d;
	}

	void BSPPatchStitchPoint::FindAlias(BSPPatchStitchPoint *sp, unsigned int numStitchPoints)
	{
		for(int i=0;i<3;i++)
			positions[i] = FVec3(verts[vertStride*i].position);

		while(numStitchPoints)
		{
			if(positions[0] == sp->positions[0] && positions[1] == sp->positions[1] && positions[2] == sp->positions[2])
			{
				alias = sp;
				return;
			}

			// Check for reverse-order alias
			if(positions[0] == sp->positions[2] && positions[1] == sp->positions[1] && positions[2] == sp->positions[0])
			{
				alias = sp;
				return;
			}
			numStitchPoints--;
			sp++;
		}

		// No match
		alias = NULL;
	}

	void BSPPatchEvaluation::TouchBounds(const FVec3 &mins, const FVec3 &maxs)
	{
		for(int i=0;i<3;i++)
		{
			if(associatedMins[i] > mins[i]) associatedMins[i] = mins[i];
			if(associatedMaxs[i] < maxs[i]) associatedMaxs[i] = maxs[i];
		}
	}


	void DimensionsForEvals(BSPPatchEvaluation *pe)
	{
		unsigned int resultWidth, resultHeight;
		const BSPFace_D *face = pe->face;

		resultWidth = resultHeight = 1;
		for(int col=0;col<face->patchDimensions[0]-1;col+=2)
			resultWidth += pe->colStitchPoints[col].divisions;
		for(int row=0;row<face->patchDimensions[1]-1;row+=2)
			resultHeight += pe->rowStitchPoints[row].divisions;

		// Calculate the vert/tri counts for this
		pe->totalCols = resultWidth;
		pe->totalRows = resultHeight;
	}

	void ComputePatch(BSPPatchEvaluation *peval, const BSPVertex_D *cpVerts, int lod)
	{
		unsigned int *outTris;
		unsigned int divisions[2];
		int idx;
		int resultWidth, resultHeight, resultNumVerts;
		float tx, ty;
		const BSPVertex_D *cps[9];
		FVec3 positionCPs[9];
		FVec3 normalCPs[9];
		FVec2 lmTexCoordCPs[9];
		FVec2 texCoordCPs[9];
		FVec2 lmTexCoord;

		Vector<FVec3> normals;
		Vector<FVec3> positions;
		Vector<FVec2> texCoords;
		Vector<FVec3> svecs;
		Vector<FVec3> tvecs;
		Vector<float> stCompMult;

		// Reload LOD divisions
		for(int i=0;i<peval->numColStitchPoints;i++)
			peval->colStitchPoints[i].divisions = peval->lods[lod].colDivisions[i/2];
		for(int i=0;i<peval->numRowStitchPoints;i++)
			peval->rowStitchPoints[i].divisions = peval->lods[lod].rowDivisions[i/2];

		DimensionsForEvals(peval);

		resultWidth = peval->totalCols;
		resultHeight = peval->totalRows;
		resultNumVerts = resultWidth * resultHeight;

		// Allocate verts
		peval->verts.Resize(NULL, resultNumVerts);

		positions.Resize(NULL, resultNumVerts);
		normals.Resize(NULL, resultNumVerts);
		svecs.Resize(NULL, resultNumVerts);
		tvecs.Resize(NULL, resultNumVerts);
		stCompMult.Resize(NULL, resultNumVerts);
		texCoords.Resize(NULL, resultNumVerts);

		// Initialize normals/svecs to zero
		for(int i=0;i<resultNumVerts;i++)
		{
			svecs[i] = FVec3(0, 0, 0);
			tvecs[i] = FVec3(0, 0, 0);
			normals[i] = FVec3(0, 0, 0);
		}


		// Evaluate the points grid
		idx = 0;
		for(int segY=0; segY<peval->rows-1; segY+=2)
		{
			divisions[1] = peval->rowStitchPoints[segY].divisions;
			for(unsigned int subY=0;subY<=divisions[1];subY++)
			{
				// First row needs 0 evaluated
				if(subY == 0 && segY != 0)
					continue;

				ty = (float)subY / (float)divisions[1];

				for(int segX=0; segX<peval->cols-1; segX+=2)
				{
					// Build control point list
					// FIXME: This should be be done differently, it's only rebuilt even for subY's because
					// divisions used to be static
					cps[0] = cpVerts + segX + segY * peval->cols;
					cps[1] = cps[0]+1; cps[2] = cps[1]+1;

					cps[3] = cpVerts + segX + (segY+1) * peval->cols;
					cps[4] = cps[3]+1; cps[5] = cps[4]+1;

					cps[6] = cpVerts + segX + (segY+2) * peval->cols;
					cps[7] = cps[6]+1; cps[8] = cps[7]+1;

					for(int i=0;i<9;i++)
					{
						positionCPs[i] = FVec3(cps[i]->position);
						normalCPs[i] = FVec3(cps[i]->normal);
						texCoordCPs[i] = FVec2(cps[i]->texCoords);
						lmTexCoordCPs[i] = FVec2(cps[i]->lightMapTexCoords);
					}

					divisions[0] = peval->colStitchPoints[segX].divisions;

					for(unsigned int subX=0;subX<=divisions[0];subX++)
					{
						// First row needs 0 evaluated
						if(subX == 0 && segX != 0)
							continue;

						tx = (float)subX / (float)divisions[0];

						// Solve components
						positions[idx] = peval->verts[idx].position = BSPEvaluate2DSpline<FVec3>(positionCPs, tx, ty);
						texCoords[idx] = peval->verts[idx].texCoords = BSPEvaluate2DSpline<FVec2>(texCoordCPs, tx, ty);

						lmTexCoord = BSPEvaluate2DSpline<FVec2>(lmTexCoordCPs, tx, ty);
						peval->verts[idx].lightmapTexCoords = lmTexCoord;

						FVec3 horizDir = BSPEvaluate2DSplineDerivativeHorizontal(positionCPs, tx, ty).Normalize2();
						FVec3 vertDir = BSPEvaluate2DSplineDerivativeVertical(positionCPs, tx, ty).Normalize2();
						FVec2 sDir = BSPEvaluate2DSplineDerivativeHorizontal(texCoordCPs, tx, ty);
						FVec2 tDir = BSPEvaluate2DSplineDerivativeVertical(texCoordCPs, tx, ty);
						FVec3 normal, svec, tvec;

						normal = horizDir.Cross(vertDir).Normalize2();

						if(normal == FVec3(0, 0, 0))
						{
							// Singularity, try to resolve it by nudging towards the center
							float newTX = tx * 0.98f + 0.01f;
							float newTY = ty * 0.98f + 0.01f;

							horizDir = BSPEvaluate2DSplineDerivativeHorizontal(positionCPs, newTX, newTY).Normalize2();
							vertDir = BSPEvaluate2DSplineDerivativeVertical(positionCPs, newTX, newTY).Normalize2();
							sDir = BSPEvaluate2DSplineDerivativeHorizontal(texCoordCPs, newTX, newTY);
							tDir = BSPEvaluate2DSplineDerivativeVertical(texCoordCPs, newTX, newTY);

							normal = horizDir.Cross(vertDir).Normalize2();
						}

						ComputeSTVecs(normal, horizDir, vertDir, sDir, tDir, &svec, &tvec);

						normals[idx] += normal;
						svecs[idx] += svec;
						tvecs[idx] += tvec;

						idx++;
					}
				}
			}
		}

		// Normalize directions
		for(int i=0;i<resultNumVerts;i++)
		{
			normals[i] = normals[i].Normalize2();
			svecs[i] = svecs[i].Normalize2();
			tvecs[i] = tvecs[i].Normalize2();
			stCompMult[i] = CompressSTVec(normals[i], svecs[i], tvecs[i]);
		}

		// Allocate tris
		peval->tris.Resize(NULL, (resultWidth - 1) * (resultHeight - 1) * 6);
		outTris = peval->tris;

		for(int segY=0;segY<resultHeight-1;segY++)
		{
			for(int segX=0;segX<resultWidth-1;segX++)
			{
				unsigned int corners[4];
				corners[0] = segX + segY * resultWidth;
				corners[1] = corners[0]+1;
				corners[2] = segX + (segY+1) * resultWidth;
				corners[3] = corners[2]+1;

				// Emit tris
				if((segX + segY) & 1)
				{
					outTris[0] = corners[0]; outTris[1] = corners[2]; outTris[2] = corners[1];
					outTris[3] = corners[1]; outTris[4] = corners[2]; outTris[5] = corners[3];
				}
				else
				{
					outTris[0] = corners[0]; outTris[1] = corners[3]; outTris[2] = corners[1];
					outTris[3] = corners[0]; outTris[4] = corners[2]; outTris[5] = corners[3];
				}
				outTris += 6;
			}
		}

		// Compute S/T vectors
		/*
		for(i=0;i<resultNumVerts;i++)
		{
			svecs[i] = FVec3(0, 0, 0);
			tvecs[i] = FVec3(0, 0, 0);
			normals[i] = FVec3(0, 0, 0);
		}
		terAccumulateNormalsAndSTVecs(positions, texCoords, peval->tris, peval->tris.Count() / 3, normals, svecs, tvecs);

		terFinalizeNormalsAndSTVecs(svecs, tvecs, resultNumVerts, normals, stvecs);
		*/

		// Store out the S/T vectors and normals
		for(int i=0;i<resultNumVerts;i++)
		{
			peval->verts[i].svec = svecs[i];
			peval->verts[i].tvecN = tvecs[i].Join(stCompMult[i]);
		}

		// Remove degenerate triangles
		unsigned int *inTris = peval->tris;
		outTris = peval->tris;
		for(LargeUInt i=0;i<peval->tris.Count();i+=3)
		{
			FVec3 points[3];
			unsigned int j;

			for(j=0;j<3;j++)
				points[j] = peval->verts[inTris[j]].position;

			if( (points[0] - points[1]).Cross(points[2] - points[1]).LengthSquared() > 0.01 )
				for(j=0;j<3;j++)
					*outTris++ = inTris[j];

			inTris += 3;
		}
		peval->tris.Resize(NULL, outTris - peval->tris);
	}


	unsigned int BSPGetPatchDivisionsInv(const BSPVertex_D *cpVerts, unsigned int primaryStride, unsigned int subStride, float maxTolerance)
	{
		unsigned int subdivs = 1;

		FVec3 cpPoints[9];

		for(int p=0;p<3;p++)
			for(int s=0;s<3;s++)
				cpPoints[p*3+s] = FVec3(cpVerts[p*primaryStride + s*subStride].position);

		// Try edge cases
		unsigned int newSubdivs = BSPGetPatchDivisionsLinear(cpPoints[0], cpPoints[1], cpPoints[2], maxTolerance);
		if(newSubdivs > subdivs) subdivs = newSubdivs;
		newSubdivs = BSPGetPatchDivisionsLinear(cpPoints[6], cpPoints[7], cpPoints[8], maxTolerance);
		if(newSubdivs > subdivs) subdivs = newSubdivs;

		// Try middle case
		cpPoints[3] = BSPEvaluateSpline<FVec3>(cpPoints[0], cpPoints[3], cpPoints[6], 0.5f);
		cpPoints[4] = BSPEvaluateSpline<FVec3>(cpPoints[1], cpPoints[4], cpPoints[7], 0.5f);
		cpPoints[5] = BSPEvaluateSpline<FVec3>(cpPoints[2], cpPoints[5], cpPoints[8], 0.5f);

		newSubdivs = BSPGetPatchDivisionsLinear(cpPoints[3], cpPoints[4], cpPoints[5], maxTolerance);
		if(newSubdivs > subdivs) subdivs = newSubdivs;

		return subdivs;
	}

	void BSPGetPatchDivisions(const BSPVertex_D *cpVerts, unsigned int stride, unsigned int *out, float maxTolerance)
	{
		out[0] = BSPGetPatchDivisionsInv(cpVerts, stride, 1, maxTolerance);
		out[1] = BSPGetPatchDivisionsInv(cpVerts, 1, stride, maxTolerance);
	}

	void ComputeLODForTolerance(const BSPModel_D *model,
		BSPPatchEvaluation *pe,
		const BSPFace_D *face,
		const BSPVertex_D *rawVerts,
		float tolerance)
	{
		// Determine divisions for this
		for(int row=0;row<face->patchDimensions[1]-1;row+=2)
			for(int col=0;col<face->patchDimensions[0]-1;col+=2)
			{
				unsigned int divisions[2];

				// Determine divisions for this subsection
				BSPGetPatchDivisions(rawVerts + face->vertex + col + row*face->patchDimensions[0], face->patchDimensions[0], divisions, tolerance);

				// Update stitch points
				pe->colStitchPoints[col].TouchDivisions(divisions[0]);
				pe->colStitchPoints[col+1].TouchDivisions(divisions[0]);
				pe->rowStitchPoints[row].TouchDivisions(divisions[1]);
				pe->rowStitchPoints[row+1].TouchDivisions(divisions[1]);
			}
	}

	void ConnectStitchPoints(BSPPatchStitchPoint *stitchPoints,
		unsigned int numStitchPoints,
		BSPPatchEvaluation *patchEvals,
		unsigned int numPatches,
		bool firstRound)
	{
		bool stitchPointsConnected = true;
		while(stitchPointsConnected)
		{
			stitchPointsConnected = false;

			// Connect stitch points aliased to other stitch points
			for(unsigned int i=0;i<numStitchPoints;i++)
			{
				BSPPatchStitchPoint *stitchPoint = stitchPoints + i;
				BSPPatchStitchPoint *stitchPointAlias = stitchPoint->alias;
				if(stitchPointAlias)
				{
					if(firstRound &&
						(stitchPointAlias->parent->associatedMaxs != stitchPoint->parent->associatedMaxs ||
						stitchPointAlias->parent->associatedMins != stitchPoint->parent->associatedMins)
						)
					{
						stitchPointsConnected = true;
						stitchPointAlias->parent->TouchBounds(stitchPoint->parent->associatedMins, stitchPoint->parent->associatedMaxs);
						stitchPoint->parent->TouchBounds(stitchPointAlias->parent->associatedMins, stitchPointAlias->parent->associatedMaxs);
					}

					if(stitchPointAlias->divisions != stitchPoint->divisions)
					{
						stitchPointsConnected = true;
						stitchPointAlias->TouchDivisions(stitchPoint->divisions);
						stitchPoint->TouchDivisions(stitchPointAlias->divisions);
					}
				}
			}

			// Connect stitch points on the same patch
			for(unsigned int i=0;i<numPatches;i++)
			{
				BSPPatchEvaluation *pe = patchEvals + i;

				for(int col=0;col<pe->cols-1;col+=2)
				{
					if(pe->colStitchPoints[col].divisions != pe->colStitchPoints[col+1].divisions)
					{
						pe->colStitchPoints[col].TouchDivisions(pe->colStitchPoints[col+1].divisions);
						pe->colStitchPoints[col+1].TouchDivisions(pe->colStitchPoints[col].divisions);
						stitchPointsConnected = true;
					}
				}

				for(int row=0;row<pe->rows-1;row+=2)
				{
					if(pe->rowStitchPoints[row].divisions != pe->rowStitchPoints[row+1].divisions)
					{
						pe->rowStitchPoints[row].TouchDivisions(pe->rowStitchPoints[row+1].divisions);
						pe->rowStitchPoints[row+1].TouchDivisions(pe->rowStitchPoints[row].divisions);
						stitchPointsConnected = true;
					}
				}
			}
		}
	}

	void CalcPatchDivisions(PatchCalculator *pc,
		const BSPModel_D *model,
		const BSPVertex_D *rawVerts)
	{
		for(unsigned int ll=0;ll<NumLODLevels_k;ll++)
		{
			float lodScalar = PatchLODBase_k + (PatchLODTolerancePerLevel_k * ((float)ll));
			float lodTolerance = lodScalar * PatchLODScale_k;

			// Clear all stitchpoint divisions
			for(unsigned int j=0;j<pc->stitchPoints.Count();j++)
				pc->stitchPoints[j].divisions = 1;

			// Clear all patch evals
			for(unsigned int j=0;j<pc->patchEvals.Count();j++)
				ComputeLODForTolerance(model, pc->patchEvals + j, pc->patchEvals[j].face,
					rawVerts, lodTolerance);

			ConnectStitchPoints(pc->stitchPoints, pc->stitchPoints.Count(), pc->patchEvals, pc->patchEvals.Count(), ll == 0);

			// Cache divisions
			for(unsigned int i=0;i<pc->patchEvals.Count();i++)
			{
				BSPPatchEvaluation *pe = pc->patchEvals + i;
				bool isAlias = true;

				StitchPointLOD *lod = pe->lods + ll;
				StitchPointLOD *prevLod = pe->lods + ll - 1;

				// Cache divisions
				lod->rowDivisions.Resize(NULL, pe->numRowStitchPoints/2);
				for(int sp=0;sp<pe->numRowStitchPoints;sp+=2)
					lod->rowDivisions[sp/2] = pe->rowStitchPoints[sp].divisions;

				lod->colDivisions.Resize(NULL, pe->numColStitchPoints/2);
				for(int sp=0;sp<pe->numColStitchPoints;sp+=2)
					lod->colDivisions[sp/2] = pe->colStitchPoints[sp].divisions;

				if(ll != 0)
				{
					// See if division counts are equal to the previous LOD level
					isAlias = true;
					for(int sp=0;sp<pe->numColStitchPoints;sp+=2)
						if(lod->colDivisions[sp/2] != prevLod->colDivisions[sp/2])
						{
							isAlias = false;
							break;
						}

					if(isAlias)
					{
						for(int sp=0;sp<pe->numRowStitchPoints;sp+=2)
						if(lod->rowDivisions[sp/2] != prevLod->rowDivisions[sp/2])
						{
							isAlias = false;
							break;
						}
					}
				}
				else
					isAlias = false;

				lod->aliasesPrevious = isAlias;
				lod->lodMultiplier = ((float)ll) / ((float)NumLODLevels_k);
				lod->lodMultiplier *= lod->lodMultiplier;
			}
		}
	}

	void CalcPatchesForModel(PatchCalculator *pc,
		const BSPModel_D *rawModel,
		const BSPFace_D *faces,
		const Vector<BSPVertex_D> &rawVerts,
		const Vector<BSPIndex_D> &meshVerts)
	{
		// Count patches and stitch points
		unsigned int numPatches = 0;
		unsigned int numStitchPoints = 0;
		for(unsigned int i=0;i<rawModel->numFaces;i++)
		{
			const BSPFace_D *face = faces + i;
			if(face->faceType == FaceTypePatch_k)
			{
				numPatches++;
				numStitchPoints += (face->patchDimensions[0] - 1) + (face->patchDimensions[1] - 1);
			}
		}

		pc->stitchPoints.Resize(NULL, numStitchPoints);
		pc->patchEvals.Resize(NULL, numPatches);

		// Create patches and stitch points
		numPatches = 0;
		numStitchPoints = 0;
		for(unsigned int i=0;i<rawModel->numFaces;i++)
		{
			const BSPFace_D *face = faces + i;
			if(face->faceType == FaceTypePatch_k)
			{
				BSPPatchStitchPoint *sp;
				BSPPatchEvaluation *pe = pc->patchEvals + numPatches;

				pe->cols = face->patchDimensions[0];
				pe->rows = face->patchDimensions[1];

				// Find extents
				FVec3 mins, maxs;
				mins = maxs = FVec3(rawVerts[face->vertex].position);
				{
					unsigned int nVerts = pe->rows * pe->cols;
					for(unsigned int i=1;i<nVerts;i++)
					{
						FVec3 v(rawVerts[face->vertex + i].position);
						for(int c=0;c<3;c++)
						{
							if(mins[c] > v[c]) mins[c] = v[c];
							if(maxs[c] < v[c]) maxs[c] = v[c];
						}
					}
				}

				pe->numColStitchPoints = (pe->cols - 1);
				pe->numRowStitchPoints = (pe->rows - 1);

				// Set up stitch points
				pe->colStitchPoints = pc->stitchPoints + numStitchPoints;
				pe->face = face;

				pe->associatedMins = mins;
				pe->associatedMaxs = maxs;

				for(int col=0;col<face->patchDimensions[0]-1;col+=2)
				{
					sp = pc->stitchPoints + numStitchPoints;
					sp->verts = rawVerts + face->vertex + col;
					sp->vertStride = 1;
					sp->divisions = 1;
					sp->FindAlias(pc->stitchPoints, numStitchPoints);
					sp->parent = pe;
					numStitchPoints++;

					sp = pc->stitchPoints + numStitchPoints;
					sp->verts = rawVerts + face->vertex + (face->patchDimensions[1]-1)*face->patchDimensions[0] + col;
					sp->vertStride = 1;
					sp->divisions = 1;
					sp->FindAlias(pc->stitchPoints, numStitchPoints);
					sp->parent = pe;
					numStitchPoints++;
				}

				pe->rowStitchPoints = pc->stitchPoints + numStitchPoints;

				for(int row=0;row<face->patchDimensions[1]-1;row+=2)
				{
					sp = pc->stitchPoints + numStitchPoints;
					sp->verts = rawVerts + face->vertex + row*face->patchDimensions[0];
					sp->vertStride = face->patchDimensions[0];
					sp->divisions = 1;
					sp->FindAlias(pc->stitchPoints, numStitchPoints);
					sp->parent = pe;
					numStitchPoints++;

					sp = pc->stitchPoints + numStitchPoints;
					sp->verts = rawVerts + face->vertex + (face->patchDimensions[0]-1) + row*face->patchDimensions[0];
					sp->vertStride = face->patchDimensions[0];
					sp->divisions = 1;
					sp->FindAlias(pc->stitchPoints, numStitchPoints);
					sp->parent = pe;
					numStitchPoints++;
				}

				numPatches++;
			}
		}

		CalcPatchDivisions(pc, rawModel, rawVerts);

		// Find associated bounds for all patch points
		for(unsigned int i=0;i<pc->patchEvals.Count();i++)
		{
			BSPPatchEvaluation *pe = pc->patchEvals + i;
			pe->lodOrigin = (pe->associatedMins + pe->associatedMaxs) * 0.5f;
		}
	}

};